// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`define MCSI_MAX_ADDR         13
//Define Address for each regiter
`define MCSI_SCRATCH_ADDR                 16'h00
`define MCSI_CONTROL_ADDR                 16'h04
`define MCSI_STATUS_ADDR                  16'h08
`define MCSI_LINK_ALIGN_ERR_ADDR          16'h0C
`define MCSI_FRAME_CRC_ERR_ADDR           16'h10
`define MCSI_AVMM_CRC_ERR_ADDR            16'h14
`define MCSI_AVMM_TIMEOUT_ERR_ADD         16'h18
`define MCSI_AVMM_CMPL_ERR_ADDR           16'h1C
`define MCSI_I2C_TIMEOUT_ERR_ADDR         16'h20
`define MCSI_BIST_DATA_CNT_H_ADDR         16'h24
`define MCSI_BIST_DATA_CNT_L_ADDR         16'h28
`define MCSI_BIST_ERR_CNT_ADDR            16'h2C
`define MCSI_FRAME_COMMA_ERR_CNT_ADDR     16'h30

module mcsi_csr
(
input             clk,
input             rst_n,
//AVMM Intf
input      [31:0] avmm_addr,
input             avmm_read,
input             avmm_write,
input      [31:0] avmm_wdata,
input      [ 3:0] avmm_byteen,
output reg        avmm_rdvalid,
output            avmm_waitrq,
output reg [31:0] avmm_rdata,
//Input SB signals
input             mcsi_link_aligned,
input             frame_crc_err,
input             frame_comma_err,
input             avmm_channel_crc_err,
input             avmm_channel_timeout,
input             avmm_channel_cmpl_err,
input             i2c_channel_timeout,
input             bist_err,
input      [3:0]  tx_frm_offset,
//Input counter
input [31:0]      avmm_crc_err_cnt,
input [31:0]      avmm_timeout_err_cnt,
input [31:0]      avmm_cmpl_err_cnt,
//Output SB signals
output            mcsi_system_rst,
output            i2c_channel_rst,
output            avmm_channel_rst,
output [1:0]      inject_frame_crc_err,
output [1:0]      inject_frame_comma_err,
output            bist_mode_enable,
//error report
output            invalid_access
);

                          
wire [15:0] addr_local; 
wire        link_align_err;
wire        negedge_link_align;
wire        posedge_i2C_timeout;

reg         invalid_read;
reg         invalid_write;

reg  [31:0] mcsi_reg [`MCSI_MAX_ADDR-1:0];      //local register
reg  [31:0] link_align_err_cnt;
reg  [31:0] frame_crc_err_cnt;
reg  [31:0] i2C_timeout_cnt;
reg  [63:0] bist_data_cnt;
reg  [31:0] bist_err_cnt;
reg  [31:0] frame_comma_err_cnt;
reg  [31:0] illegal_write;
reg         link_align_d1;
reg         link_align_d2;
reg         i2c_channel_timeout_d1;
reg         i2c_channel_timeout_d2;

assign avmm_waitrq      = 1'b0;
assign addr_local       = avmm_addr[15:0];    //Only 16 bits out of 32 bits avmm_addr supported
assign link_align_err   = (link_align_err_cnt != 32'b0);

assign invalid_access   = invalid_read || invalid_write;

always @ (posedge clk ) begin
    if ( !rst_n ) begin
        link_align_d1 <= 1'b0;
        link_align_d2 <= 1'b0;
    end else begin
        link_align_d1 <= mcsi_link_aligned;
        link_align_d2 <= link_align_d1;
    end
end

assign negedge_link_align = (~link_align_d1 & link_align_d2); //each negedge stands for a link alignment error happen

//link_align_err_cnt
always @ (posedge clk) begin
    if (!rst_n) begin
        link_align_err_cnt <= 32'b0;
    end else if ( link_align_err_cnt <= 32'hFFFF_FFFF && negedge_link_align ) begin
        link_align_err_cnt <= link_align_err_cnt + 1'b1;
    end else begin
        link_align_err_cnt <= link_align_err_cnt;
    end
end
//frame_crc_err_cnt
always @ (posedge clk) begin
    if (!rst_n) begin
        frame_crc_err_cnt <= 32'b0;
    end else if ( frame_crc_err_cnt <= 32'hFFFF_FFFF && frame_crc_err ) begin
        frame_crc_err_cnt <= frame_crc_err_cnt + 1'b1;
    end else begin
        frame_crc_err_cnt <= frame_crc_err_cnt;
    end
end

always @ (posedge clk ) begin
    if ( !rst_n ) begin
        i2c_channel_timeout_d1 <= 1'b0;
        i2c_channel_timeout_d2 <= 1'b0;
    end else begin
        i2c_channel_timeout_d1 <= i2c_channel_timeout;
        i2c_channel_timeout_d2 <= i2c_channel_timeout_d1;
    end
end

assign posedge_i2C_timeout = (i2c_channel_timeout_d1 & ~i2c_channel_timeout_d2);

//i2C_timeout_cnt
always @ (posedge clk) begin
    if (!rst_n) begin
        i2C_timeout_cnt <= 32'b0;
    end else if ( i2C_timeout_cnt <= 32'hFFFF_FFFF && posedge_i2C_timeout ) begin
        i2C_timeout_cnt <= i2C_timeout_cnt + 1'b1;
    end else begin
        i2C_timeout_cnt <= i2C_timeout_cnt;
    end
end

//bist_data_cnt
always @(posedge clk) begin
    if (~rst_n) begin
        bist_data_cnt <=64'h0;
    end else if ( bist_data_cnt <=64'hFFFF_FFFF_FFFF_FFFF && bist_mode_enable && ((tx_frm_offset >= 4'd2) && (tx_frm_offset <= 4'd10)))begin
        bist_data_cnt = bist_data_cnt + 1'b1;
    end else begin
        bist_data_cnt = bist_data_cnt;
    end
end

//bist_err_cnt
always @(posedge clk) begin
    if (~rst_n) begin
        bist_err_cnt <=32'h0;
    end else if ( bist_err_cnt <=32'hFFFF_FFFF && bist_err )begin
        bist_err_cnt = bist_err_cnt + 1'b1;
    end else begin
        bist_err_cnt = bist_err_cnt;
    end
end

//frame_comma_err_cnt
always @(posedge clk) begin
    if (~rst_n) begin
        frame_comma_err_cnt <=32'h0;
    end else if ( frame_comma_err_cnt <=32'hFFFF_FFFF && frame_comma_err )begin
        frame_comma_err_cnt = frame_comma_err_cnt + 1'b1;
    end else begin
        frame_comma_err_cnt = frame_comma_err_cnt;
    end
end

//control register output SB signal
assign mcsi_system_rst        = mcsi_reg[`MCSI_CONTROL_ADDR / 4][0];
assign i2c_channel_rst        = mcsi_reg[`MCSI_CONTROL_ADDR / 4][1];
assign avmm_channel_rst       = mcsi_reg[`MCSI_CONTROL_ADDR / 4][2];
assign bist_mode_enable       = mcsi_reg[`MCSI_CONTROL_ADDR / 4][3];
assign inject_frame_crc_err   = mcsi_reg[`MCSI_CONTROL_ADDR / 4][5:4];
assign inject_frame_comma_err = mcsi_reg[`MCSI_CONTROL_ADDR / 4][7:6];

//invalid_read, use combination logic for error report
always @ ( * ) begin
    if ( !rst_n ) begin
		  invalid_read      = 1'b0;
    end else begin
        if ( avmm_read && !( |avmm_addr[31:16]) && (&avmm_byteen)) begin
            case (addr_local)
                 `MCSI_SCRATCH_ADDR             : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_CONTROL_ADDR             : begin
                    invalid_read      = 1'b0;
					  end						  
                 `MCSI_STATUS_ADDR              : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_LINK_ALIGN_ERR_ADDR      : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_FRAME_CRC_ERR_ADDR       : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_AVMM_CRC_ERR_ADDR        : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_AVMM_TIMEOUT_ERR_ADD     : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_AVMM_CMPL_ERR_ADDR       : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_I2C_TIMEOUT_ERR_ADDR     : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_BIST_DATA_CNT_H_ADDR     : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_BIST_DATA_CNT_L_ADDR     : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_BIST_ERR_CNT_ADDR        : begin
						  invalid_read      = 1'b0;
					  end
                 `MCSI_FRAME_COMMA_ERR_CNT_ADDR : begin
                    invalid_read      = 1'b0;
					  end						  
                 default                        : begin
						  invalid_read      = 1'b1;
					  end
            endcase
        end
		  else begin
		     invalid_read               = 1'b0;
		  end
    end
end

//read registers
always @ ( posedge clk ) begin
    if ( !rst_n ) begin
        avmm_rdata       <= 32'h0;
    end else begin
        if ( avmm_read && !( |avmm_addr[31:16]) && (&avmm_byteen)) begin
            case (addr_local)
                 `MCSI_SCRATCH_ADDR             : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_SCRATCH_ADDR         / 4];
					  end
                 `MCSI_CONTROL_ADDR             : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_CONTROL_ADDR         / 4]; 
					  end						  
                 `MCSI_STATUS_ADDR              : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_STATUS_ADDR          / 4];
					  end
                 `MCSI_LINK_ALIGN_ERR_ADDR      : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_LINK_ALIGN_ERR_ADDR  / 4];
					  end
                 `MCSI_FRAME_CRC_ERR_ADDR       : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_FRAME_CRC_ERR_ADDR   / 4];
					  end
                 `MCSI_AVMM_CRC_ERR_ADDR        : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_AVMM_CRC_ERR_ADDR    / 4];
					  end
                 `MCSI_AVMM_TIMEOUT_ERR_ADD     : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_AVMM_TIMEOUT_ERR_ADD / 4];
					  end
                 `MCSI_AVMM_CMPL_ERR_ADDR       : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_AVMM_CMPL_ERR_ADDR   / 4];
					  end
                 `MCSI_I2C_TIMEOUT_ERR_ADDR     : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_I2C_TIMEOUT_ERR_ADDR / 4];
					  end
                 `MCSI_BIST_DATA_CNT_H_ADDR     : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_BIST_DATA_CNT_H_ADDR / 4];
					  end
                 `MCSI_BIST_DATA_CNT_L_ADDR     : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_BIST_DATA_CNT_L_ADDR / 4];
					  end
                 `MCSI_BIST_ERR_CNT_ADDR        : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_BIST_ERR_CNT_ADDR    / 4];
					  end
                 `MCSI_FRAME_COMMA_ERR_CNT_ADDR : begin
					     avmm_rdata       <= mcsi_reg[`MCSI_FRAME_COMMA_ERR_CNT_ADDR    / 4];
					  end						  
                 default                        : begin
					     avmm_rdata       <= 32'hFFFF_FFFF; //reture bad addr
					  end
            endcase
        end
    end
end

//readvalid asserts one clock cycle after read assetted.
always @ ( posedge clk ) begin
    if ( !rst_n ) begin
        avmm_rdvalid <= 1'b0;
    end else begin
        if ( avmm_read && (&avmm_byteen) && (!invalid_read)) begin
            avmm_rdvalid <= 1'b1; 
        end else begin
            avmm_rdvalid <= 1'b0;
        end
    end
end

//invalid_write, use combination logic for error report
always @ ( * ) begin
    if ( !rst_n ) begin
		  invalid_write    = 1'b0;
    end else begin
        if ( avmm_write && !( |avmm_addr[31:16]) && (&avmm_byteen)) begin
            case (addr_local)
                `MCSI_SCRATCH_ADDR             : begin
                   invalid_write                               = 1'b0;
                end
                `MCSI_CONTROL_ADDR             : begin
                   invalid_write                               = 1'b0;
                end						 
                `MCSI_STATUS_ADDR              : begin
                   invalid_write                               = 1'b0;
                end						 
                `MCSI_LINK_ALIGN_ERR_ADDR      : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_FRAME_CRC_ERR_ADDR       : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_AVMM_CRC_ERR_ADDR        : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_AVMM_TIMEOUT_ERR_ADD     : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_AVMM_CMPL_ERR_ADDR       : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_I2C_TIMEOUT_ERR_ADDR     : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_BIST_DATA_CNT_H_ADDR     : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_BIST_DATA_CNT_L_ADDR     : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_BIST_ERR_CNT_ADDR        : begin
						 invalid_write                               = 1'b0;
                end
                `MCSI_FRAME_COMMA_ERR_CNT_ADDR : begin
                   invalid_write                               = 1'b0;
                end						 
                default                        : begin
						 invalid_write                               = 1'b1;
                end
            endcase
        end
		  else begin
		     invalid_write                    = 1'b0;
		  end         		  
    end
end

//Wrire Register
//Sticky for specific bits, when clear and sticky err asserts same time, do clear first and then record sticky err
integer i;
always @ ( posedge clk ) begin
    if ( !rst_n ) begin
        for ( i=0; i<=`MCSI_MAX_ADDR-1; i++ ) begin
            mcsi_reg[i]  <= 32'h0;
        end
    end else begin
        //latch un-sticky err
        mcsi_reg[`MCSI_STATUS_ADDR         /4]     <= mcsi_reg[`MCSI_STATUS_ADDR/4] & {31'h7FFF_FFFF, mcsi_link_aligned};      
        mcsi_reg[`MCSI_LINK_ALIGN_ERR_ADDR /4]     <= link_align_err_cnt  ;
        mcsi_reg[`MCSI_FRAME_CRC_ERR_ADDR  /4]     <= frame_crc_err_cnt   ;
        mcsi_reg[`MCSI_AVMM_CRC_ERR_ADDR   /4]     <= avmm_crc_err_cnt    ;
        mcsi_reg[`MCSI_AVMM_TIMEOUT_ERR_ADD/4]     <= avmm_timeout_err_cnt;
        mcsi_reg[`MCSI_AVMM_CMPL_ERR_ADDR  /4]     <= avmm_cmpl_err_cnt   ;
        mcsi_reg[`MCSI_I2C_TIMEOUT_ERR_ADDR/4]     <= i2C_timeout_cnt     ;
        mcsi_reg[`MCSI_BIST_DATA_CNT_H_ADDR/4]     <= bist_data_cnt[63:32];
        mcsi_reg[`MCSI_BIST_DATA_CNT_L_ADDR/4]     <= bist_data_cnt[31: 0];       
        mcsi_reg[`MCSI_BIST_ERR_CNT_ADDR   /4]     <= bist_err_cnt        ;
		  mcsi_reg[`MCSI_FRAME_COMMA_ERR_CNT_ADDR/4] <= frame_comma_err_cnt ;
                
        //write one clear
        if ( avmm_write && !( |avmm_addr[31:16]) && (&avmm_byteen)) begin
            case (addr_local)
                `MCSI_SCRATCH_ADDR             : begin
					    mcsi_reg[`MCSI_SCRATCH_ADDR        /4]     <= avmm_wdata;
                end
                `MCSI_CONTROL_ADDR             : begin
					    mcsi_reg[`MCSI_CONTROL_ADDR        /4]     <= avmm_wdata;
                end						 
                `MCSI_STATUS_ADDR              : begin
					    mcsi_reg[`MCSI_STATUS_ADDR         /4]     <= mcsi_reg[`MCSI_STATUS_ADDR/4] & ( ~{23'b0, avmm_wdata[8:1], 1'b0});
                end						 
                `MCSI_LINK_ALIGN_ERR_ADDR      : begin
					    mcsi_reg[`MCSI_LINK_ALIGN_ERR_ADDR /4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_LINK_ALIGN_ERR_ADDR /4];
                end
                `MCSI_FRAME_CRC_ERR_ADDR       : begin
					    mcsi_reg[`MCSI_FRAME_CRC_ERR_ADDR  /4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_FRAME_CRC_ERR_ADDR  /4];
                end
                `MCSI_AVMM_CRC_ERR_ADDR        : begin
					    mcsi_reg[`MCSI_AVMM_CRC_ERR_ADDR   /4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_AVMM_CRC_ERR_ADDR   /4];
                end
                `MCSI_AVMM_TIMEOUT_ERR_ADD     : begin
					    mcsi_reg[`MCSI_AVMM_TIMEOUT_ERR_ADD/4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_AVMM_TIMEOUT_ERR_ADD/4];
                end
                `MCSI_AVMM_CMPL_ERR_ADDR       : begin
					    mcsi_reg[`MCSI_AVMM_CMPL_ERR_ADDR  /4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_AVMM_CMPL_ERR_ADDR  /4];
                end
                `MCSI_I2C_TIMEOUT_ERR_ADDR     : begin
					    mcsi_reg[`MCSI_I2C_TIMEOUT_ERR_ADDR/4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_I2C_TIMEOUT_ERR_ADDR/4];
                end
                `MCSI_BIST_DATA_CNT_H_ADDR     : begin
					    mcsi_reg[`MCSI_BIST_DATA_CNT_H_ADDR/4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_BIST_DATA_CNT_H_ADDR/4];
                end
                `MCSI_BIST_DATA_CNT_L_ADDR     : begin
					    mcsi_reg[`MCSI_BIST_DATA_CNT_L_ADDR/4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_BIST_DATA_CNT_L_ADDR/4];
                end
                `MCSI_BIST_ERR_CNT_ADDR        : begin
					    mcsi_reg[`MCSI_BIST_ERR_CNT_ADDR   /4]     <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_BIST_ERR_CNT_ADDR   /4];
                end
                `MCSI_FRAME_COMMA_ERR_CNT_ADDR : begin
					    mcsi_reg[`MCSI_FRAME_COMMA_ERR_CNT_ADDR/4] <= avmm_wdata[0]? 32'h0: mcsi_reg[`MCSI_FRAME_COMMA_ERR_CNT_ADDR/4];
                end						 
                default                        : begin
					    illegal_write                              <= avmm_wdata;
                end
            endcase
        end
        //Status_Reg, record sticky err
        if (link_align_err)                 
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 1);//bit_1
        if (frame_crc_err)                                                    
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 2);//bit_2 
        if (avmm_channel_crc_err)                                               
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 3);//bit_3
        if (avmm_channel_timeout)                                               
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 4);//bit_4 
        if (avmm_channel_cmpl_err)                                              
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 5);//bit_5
        if (i2c_channel_timeout)                                                
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 6);//bit_6
        if (bist_err)                                                         
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 7);//bit_7
        if (frame_comma_err)
            mcsi_reg[`MCSI_STATUS_ADDR/4]  <= mcsi_reg[`MCSI_STATUS_ADDR/4] | (32'b1 << 8);//bit_8            		  
    end
end
                                                                                                                                                       
endmodule
